Skip to content

fix(apply-repo-settings): normalize bypass_actors order before diffing - #95

Merged
nsheaps merged 2 commits into
mainfrom
nate-ai/normalize-ruleset-array-diff
Jul 27, 2026
Merged

fix(apply-repo-settings): normalize bypass_actors order before diffing#95
nsheaps merged 2 commits into
mainfrom
nate-ai/normalize-ruleset-array-diff

Conversation

@nsheaps-oura

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes a permanent false-positive drift report in apply-repo-settings's ruleset sync, traced from nsheaps/.github#103 / #45 (an investigation into whether the automation app was actually configured as a require-pr bypass actor on nsheaps/greasemonkey-scripts).
  • Conclusion of that investigation: there was no real bypass_actors gap — the automation app was already correctly configured and had already successfully pushed directly to greasemonkey-scripts main under this exact ruleset. The apparent "still needs update" signal was a false positive in this action's drift-detection logic.

Root cause

apply_rulesets()'s current-vs-desired comparison does:

current_norm="$(gh api ".../rulesets/${id}" --jq '{name, target, enforcement, conditions, rules, bypass_actors}' | jq -S '.')"
desired_norm="$(echo "$body" | jq -S '{name, target, enforcement, conditions, rules, bypass_actors}')"

jq -S sorts object keys, not array element order. GitHub doesn't guarantee bypass_actors[] comes back in the same order settings.yml declares them, so when the live and desired sets are identical but differently ordered, the plain string comparison reports drift forever.

Confirmed via workflow run 30285386587 (dry-run dispatch of apply-repo-settings.yaml on nsheaps/greasemonkey-scripts): the diff for require-pr (and identically for require-1-review, require-codeowner-review) showed the same two bypass actors present on both sides, just at swapped array positions — not a missing actor.

Fix

Sort bypass_actors by a stable key (actor_type, actor_id) on both current_norm and desired_norm before the jq -S comparison, so ordering differences no longer register as drift.

Test plan

  • bash -n — syntax check passes.
  • shellcheck .github/actions/apply-repo-settings/action.sh — clean, no warnings.
  • Extracted the exact ruleset_filter jq expression and ran it standalone against synthetic JSON reproducing the reported scenario (two bypass actors present on both sides, swapped order) — confirms current_norm == desired_norm after the fix (previously would differ).
  • Ran the same filter against a genuine-gap case (one side missing an actor entirely) — confirms real drift is still correctly detected, i.e. the fix doesn't mask actual config gaps.
  • Not yet exercised end-to-end against a live repo from this branch (this action is only invoked via uses: nsheaps/github-actions/.../apply-repo-settings@main by consumers, so a live check requires either this landing on main or a consumer temporarily pointing at this branch — first live exercise will be the next real apply-repo-settings dry-run/apply run once merged).

Known separate issue (not fixed here)

The same dry-run log also showed allowed_merge_methods/required_reviewers: [] flip-flopping between current and desired — GitHub backfills some rule parameters with defaults that settings.yml never explicitly sets, causing a second, unrelated false-positive. Deliberately left out of this change to avoid guessing at GitHub's per-rule-type default-fill behavior without more research; flagged as a follow-up.

🤖 Generated with Claude Code

nsheaps-oura and others added 2 commits July 27, 2026 13:10
jq -S only sorts object keys, not array element order. GitHub doesn't
guarantee bypass_actors[] comes back in the same order settings.yml
declares them, so the ruleset drift check flagged a permanent false
"needs update" whenever the live and desired bypass_actors sets were
identical but differently ordered — observed identically across
require-pr, require-1-review, and require-codeowner-review on
nsheaps/greasemonkey-scripts (workflow run 30285386587), traced back
from nsheaps/.github#103/#45.

Sort bypass_actors by (actor_type, actor_id) on both sides before the
jq -S comparison so ordering differences no longer register as drift.
Verified against the exact reported scenario (swapped actor_id order)
and against a genuine-gap case (missing actor) to confirm real drift
is still detected.

Co-Authored-By: Claude Code (~/src/nsheaps/github-actions) <noreply@anthropic.com>
@nsheaps
nsheaps merged commit c7525e9 into main Jul 27, 2026
2 checks passed
@nsheaps
nsheaps deleted the nate-ai/normalize-ruleset-array-diff branch July 27, 2026 17:18
nsheaps-oura added a commit that referenced this pull request Jul 27, 2026
…aults before diffing

GitHub backfills two optional pull_request rule parameters that
settings.yml often leaves unset: allowed_merge_methods (all three
methods — merge, squash, rebase — regardless of what the repo itself
allows) and required_reviewers (empty array). Neither default is
declared in GitHub's OpenAPI schema or REST docs, so the ruleset diff
treated their absence in settings.yml as a permanent mismatch against
the live API response, even when nothing was actually misconfigured.

Confirmed these defaults empirically rather than assuming them: read
live rulesets on both nsheaps/greasemonkey-scripts and nsheaps/.github
(which independently have allow_merge_commit:false at the repo level,
yet both still return "merge" in allowed_merge_methods), and cross-
checked GitHub's REST docs + OpenAPI schema, which state no default
for either field.

Backfill both fields on current vs. desired before the jq -S
comparison (stacked on #95's ruleset-diff fix),
only when actually absent so a genuine future restriction still
registers as drift.

Co-Authored-By: Claude Code (~/src/nsheaps/github-actions) <noreply@anthropic.com>
nsheaps-oura added a commit that referenced this pull request Jul 27, 2026
…aults before diffing (#96)

GitHub backfills two optional pull_request rule parameters that
settings.yml often leaves unset: allowed_merge_methods (all three
methods — merge, squash, rebase — regardless of what the repo itself
allows) and required_reviewers (empty array). Neither default is
declared in GitHub's OpenAPI schema or REST docs, so the ruleset diff
treated their absence in settings.yml as a permanent mismatch against
the live API response, even when nothing was actually misconfigured.

Confirmed these defaults empirically rather than assuming them: read
live rulesets on both nsheaps/greasemonkey-scripts and nsheaps/.github
(which independently have allow_merge_commit:false at the repo level,
yet both still return "merge" in allowed_merge_methods), and cross-
checked GitHub's REST docs + OpenAPI schema, which state no default
for either field.

Backfill both fields on current vs. desired before the jq -S
comparison (stacked on #95's ruleset-diff fix),
only when actually absent so a genuine future restriction still
registers as drift.

Co-authored-by: Claude Code (~/src/nsheaps/github-actions) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants